

public class Assignment5Test {

	/**
	 * A basic testing code for assignment 5. See the
	 * TestOutput.tex file in the course web-site for
	 * expected output. Note that different systems may
	 * represent {@code double} values slightly different, and that
	 * the order of sub-expressions might change for
	 * different derivative implementations.
	 * @throws Exception 
	 */
	public static void main(String[] args) throws Exception {
		Expression x = new VariableExpression('x');
		Expression y = new VariableExpression('y');
		Expression z = new VariableExpression('z');

		Expression exp1 = new Addition(x, y);
		Expression[] arg2 = {x, y, z, y};
		Expression exp2 = new LongSum(arg2);
		Expression exp3 = new Multiplication(exp1, exp2);
		Expression exp4 = new Sine(exp3);
		Expression exp5 = new Cosine(exp3);
		Expression[] arg6 = {x, z, z, y};
		Expression exp6 = new LongMultiplication(arg6);

		Expression exp7 = new UnaryMinus(exp6);

		Expression exp8 = exp7.derivative('x'); 
		Expression exp9 = exp7.derivative('y'); 
		Expression exp10 = exp5.derivative('z'); 
		Expression exp11 = exp6.derivative('y'); 

		Expression[] expressions = {
				exp1, exp2, exp3, exp4,
				exp5, exp6, exp7, exp8,
				exp9, exp10, exp11
		};

		Assignment assign1 = new Assignment();
		assign1.setVarValue('x', 1.1);
		assign1.setVarValue('y', 2.2);
		assign1.setVarValue('z', -3.3);

		System.out.println("First assignment values:");
		printAssignment(assign1);
		System.out.println("\nExpression values under the first assignment:");
		printExpressionArray(expressions, assign1);

		Assignment assign2 = new Assignment();
		assign2.setVarValue('x', -3);
		assign2.setVarValue('y', 4);
		assign2.setVarValue('z', 5);

		System.out.println();
		System.out.println("Second assignment values:");
		printAssignment(assign2);
		System.out.println("\nExpression values under the second assignment:");
		printExpressionArray(expressions, assign2);


		Expression exp12 = new Addition(x, y);
		Expression exp13 = new Addition(y, x);  
		Expression exp14 = new Multiplication(x, y);
		Expression exp15 = new HackedExpression(exp1);

		System.out.println();
		System.out.println("\nBasic equality check:");

		System.out.println("\nFirst expression:" + exp1);
		System.out.println("Second expression:" + exp12);
		System.out.println("Is first equal to second? " + exp1.equals(exp12));

		System.out.println();
		System.out.println("\nFirst expression:" + exp1);
		System.out.println("Second expression:" + exp13);
		System.out.println("Is first equal to second? " + exp1.equals(exp13));

		System.out.println();
		System.out.println("\nFirst expression:" + exp1);
		System.out.println("Second expression:" + exp14);
		System.out.println("Is first equal to second? " + exp1.equals(exp14));

		System.out.println();
		System.out.println("\nFirst expression:" + exp1);
		System.out.println("Second expression:" + exp15);
		System.out.println(
				"The second has the same string representation, but" +
				"\nit is of different class which overides the evaluate" +
				"\nmethod and always returns 0:");
		printEvaluatedExpression(exp1, assign1);
		printEvaluatedExpression(exp15, assign1);
		System.out.println("Is first equal to second? " + exp1.equals(exp15));
		
		if (exp1.equals(exp15)){
			System.out.println(
					"You have made an erroneous implementation of" +
					"\nequals, using string comparison! THIS MUST" +
					"\nBE FIXED!"); 
		}
	}

	private static void printAssignment(Assignment assign) {
		char[] variables = {'x', 'y', 'z'};
		for (char variable: variables){
			System.out.println(variable + " = " + assign.getVarValue(variable));
		}
	}

	/**
	 * Prints an array of expressions to the screen.
	 * 
	 * @param expressions an array of expressions to be printed.
	 * @param assign 
	 * @throws Exception 
	 */
	private static void printExpressionArray(Expression[] expressions, Assignment assign) throws Exception {
		for (int i=0; i<expressions.length; i = i+1){
			printEvaluatedExpression(expressions[i], assign);
		}
	}

	/**
	 * Prints an expression along with its evaluated value.
	 *  
	 * @param exp an expression to be printed.
	 * @param assign 
	 * @throws Exception 
	 */
	private static void printEvaluatedExpression(Expression exp, Assignment assign) throws Exception{
		System.out.println(exp + " = " + exp.evaluate(assign));
	}


	private static class HackedExpression implements Expression {

		private Expression exp;

		public HackedExpression(Expression exp){
			this.exp = exp;
		}

		public Expression derivative(char varName) {
			return null;
		}

		public double evaluate(Assignment assign) {
			return 0;
		}

		public String toString(){
			return exp.toString();
		}

		public boolean equals(Object other){
			return exp.equals(other);
		}

		public Expression partialAssignment(Assignment assign) {
			return null;
		}

		public Expression replicate() {
			return null;
		}

		public Expression simplifyExpression() {
			return null;
		}
	}


}
